home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 January: Mac OS SDK / Dev.CD Jan 97 SDK1.toast / Development Kits (Disc 1) / AppleScript / Development Tools / Sample Code / 7Edit 3.1 / Sources / SVAETextUtils.c < prev    next >
Encoding:
Text File  |  1995-11-20  |  18.5 KB  |  797 lines  |  [TEXT/CWIE]

  1. // SVAETextUtils.c
  2. //
  3. // 7Edit 3.1d1. Original version by Jon Lansdell and Nigel Humphreys.
  4. // 3.1 updates by Greg Sutton.
  5. // ©Apple Computer Inc 1995, all rights reserved.
  6.  
  7. #include "SVAETextUtils.h"
  8.  
  9. #include "SVEditAEUtils.h"
  10. #include "SVEditWindow.h"        // for DPtrFromWindowPtr()
  11. #include "SVAERecording.h"
  12.  
  13.  
  14. #include <AEPackObject.h>
  15.  
  16.  
  17.  
  18. // ----------------------------------------------------------------------------------
  19. //    Name:    PutStyledTextFromDescIntoTEHandle
  20. //    Purpose: Takes the text in an AEDesc containing typeIntlText and puts it in
  21. //             a styled text edit record at the current insertion point.
  22. //                     Looks for typeIntlText, typeStyledText, typeChar in that order.
  23. // ----------------------------------------------------------------------------------
  24.  
  25. OSErr    PutStyledTextFromDescIntoTEHandle(const AEDesc *sourceTextDesc, TEHandle theHTE)
  26. {
  27.     AEDesc styledTextDesc;
  28.     AEDesc textStyleDesc;
  29.     AEDesc rawTextDesc;
  30.     OSErr  myErr;
  31.     OSErr  ignoreErr;
  32.     
  33.     styledTextDesc.dataHandle = nil;
  34.     textStyleDesc.dataHandle  = nil;
  35.     rawTextDesc.dataHandle    = nil;
  36.     
  37.     //    Coerce to an AERecord and then extract the parts of the
  38.     //    styled text - works for typeIntlText, typeStyledText
  39.  
  40.     myErr = AECoerceDesc(sourceTextDesc, typeAERecord, &styledTextDesc);
  41.     
  42.     if (noErr == myErr)
  43.     {        
  44.         myErr = AEGetKeyDesc(&styledTextDesc,
  45.                                                  keyAEText,
  46.                                                  typeChar,
  47.                                                  &rawTextDesc);
  48.                                                  
  49.         myErr = AEGetKeyDesc(&styledTextDesc,
  50.                                                  keyAEStyles,
  51.                                                  typeScrapStyles,
  52.                                                  &textStyleDesc);
  53.     }
  54.     else
  55.     {
  56.         myErr = AECoerceDesc(sourceTextDesc, typeChar, &rawTextDesc);
  57.         
  58.         textStyleDesc.dataHandle = NULL; // so that TEStylInsert acts like TEInsert                
  59.     }
  60.             
  61.     HLock((Handle)rawTextDesc.dataHandle);
  62.     
  63.     TEDelete(theHTE);                    // Insert over current selection
  64.     TEStylInsert((const void *) (*rawTextDesc.dataHandle),
  65.                                          GetHandleSize(rawTextDesc.dataHandle),
  66.                                              (StScrpHandle) textStyleDesc.dataHandle,
  67.                                                  theHTE);
  68.                              
  69.     HUnlock((Handle)rawTextDesc.dataHandle);
  70.     
  71.     if (textStyleDesc.dataHandle)
  72.         ignoreErr = AEDisposeDesc(&textStyleDesc);
  73.     
  74.     if (rawTextDesc.dataHandle)
  75.         ignoreErr = AEDisposeDesc(&rawTextDesc);
  76.     
  77.     if (styledTextDesc.dataHandle)
  78.         ignoreErr = AEDisposeDesc(&styledTextDesc);
  79.         
  80.     return(myErr);
  81. }
  82.  
  83.  
  84. TEHandle    TEHandleFromWindow(WindowPtr theWindow)
  85. {
  86.     DPtr        docPtr;
  87.     TEHandle    result = NULL;
  88.     
  89.     if (! theWindow)
  90.         return(NULL);
  91.     
  92.     docPtr = DPtrFromWindowPtr(theWindow);
  93.     
  94.     if (docPtr)
  95.         result = docPtr->theText;
  96.         
  97.     return(result);
  98. }
  99.  
  100. TEHandle    TEHandleFromTextToken(TextToken* aToken)
  101. {
  102.     if (! aToken)
  103.         return(NULL);
  104.         
  105.     return(TEHandleFromWindow(aToken->tokenWindow));
  106. }
  107.  
  108.  
  109. OSErr    GetInsertDescFromInsertHere(AEDesc* insertHereDesc, AEDesc* insertDesc, DescType* insertType)
  110. {
  111.     AEDesc        insertRec = {typeNull, NULL},
  112.                 objectSpec = {typeNull, NULL};
  113.     DescType    returnedType;
  114.     Size        actualSize;
  115.     OSErr        err;
  116.  
  117.      switch (insertHereDesc->descriptorType)
  118.      {
  119.          case typeInsertionLoc:
  120.              err = AECoerceDesc(insertHereDesc, typeAERecord, &insertRec);
  121.              if (noErr != err) goto done;
  122.              
  123.              err = AEGetKeyPtr(&insertRec, keyAEPosition, typeEnumeration, &returnedType,
  124.                                          (Ptr)insertType, sizeof(insertType), &actualSize);
  125.              if (noErr != err) goto done;
  126.  
  127.             err = AEGetKeyDesc(&insertRec, keyAEObject, typeWildCard, &objectSpec);
  128.             if (objectSpec.descriptorType != typeNull)
  129.             {
  130.                 err = AEResolve(&objectSpec, kAEIDoMinimum, insertDesc);
  131.                 if (err != noErr) goto done;
  132.             }
  133.              break;
  134.      
  135.          case typeObjectSpecifier:
  136.              err = AEResolve(insertHereDesc, kAEIDoMinimum, insertDesc);
  137.              if (noErr != err) goto done;
  138.              *insertType = insertDesc->descriptorType;
  139.              break;
  140.              
  141.          case typeNull:                    // No insertion location given
  142.              *insertType = typeNull;
  143.              break;
  144.              
  145.          default:                        // Just copy the descriptor
  146.              err = AEDuplicateDesc(insertHereDesc, insertDesc);
  147.              if (noErr != err) goto done;
  148.              *insertType = insertDesc->descriptorType;
  149.      }
  150.      
  151.  done:
  152.      if (insertRec.dataHandle)
  153.          AEDisposeDesc(&insertRec);
  154.      if (objectSpec.dataHandle)
  155.          AEDisposeDesc(&objectSpec);
  156.      
  157.      return(err);
  158. }
  159.  
  160. // This routine returns an enumerated type describing the relative position
  161. // of one TextToken to another.
  162.  
  163. TokenWithinType    TokenWithinToken(TextToken* container, TextToken* token, short* numPartial)
  164. {
  165.     TokenWithinType        result;
  166.  
  167.     if (token->tokenOffset + token->tokenLength < container->tokenOffset)
  168.         result = kTokenBefore;
  169.     else if (container->tokenOffset + container->tokenLength < token->tokenOffset)
  170.         result = kTokenAfter;
  171.     else if (token->tokenOffset >= container->tokenOffset
  172.                 && token->tokenOffset + token->tokenLength <= container->tokenOffset + container->tokenLength)
  173.         result = kTokenWithin;
  174.     else if (token->tokenOffset < container->tokenOffset)
  175.     {
  176.         result = kTokenPartialBefore;
  177.         if (numPartial)
  178.             *numPartial = token->tokenOffset + token->tokenLength - container->tokenOffset;
  179.     }
  180.     else
  181.     {
  182.         result = kTokenPartialAfter;
  183.         if (numPartial)
  184.             *numPartial = container->tokenOffset + container->tokenLength - token->tokenOffset;
  185.     }
  186.     
  187.     return(result);
  188. }
  189.  
  190.  
  191. OSErr    TextTokenFromWindowToken(WindowToken* theWindowToken, TextToken* theTextToken)
  192. {
  193.     DPtr        docPtr;
  194.  
  195.     docPtr = DPtrFromWindowPtr(theWindowToken->tokenWindow);
  196.  
  197.     if (! docPtr)
  198.         return(errAENoSuchObject);
  199.             // Create our text token
  200.     theTextToken->tokenWindow = theWindowToken->tokenWindow;    
  201.     theTextToken->tokenOffset = 1;                                // Start at 1
  202.     theTextToken->tokenLength = (**docPtr->theText).teLength;    // through whole length
  203.     
  204.     return(noErr);
  205. }
  206.  
  207.  
  208. OSErr    TextTokenFromWindowDesc(AEDesc* windowDesc, TextToken* theToken)
  209. {
  210.     AEDesc            aDesc = {typeNull, NULL};
  211.     WindowToken        aWindowToken;
  212.     Size            actualSize;
  213.     OSErr            err;
  214.     
  215.     err = AECoerceDesc(windowDesc, typeMyWndw, &aDesc);
  216.     if (noErr != err) goto done;
  217.         
  218.     GetRawDataFromDescriptor(&aDesc, (Ptr)&aWindowToken,
  219.                                     sizeof(aWindowToken), &actualSize);
  220.  
  221.     err = TextTokenFromWindowToken(&aWindowToken, theToken);
  222.     
  223. done:    
  224.     if (aDesc.dataHandle)
  225.         AEDisposeDesc(&aDesc);
  226.  
  227.     return(err);
  228. }
  229.  
  230.  
  231. OSErr    TextDescFromWindowToken(WindowToken* theWindowToken, AEDesc* textDesc)
  232. {
  233.     TextToken    aToken;
  234.     OSErr        err;
  235.     
  236.     err = TextTokenFromWindowToken(theWindowToken, &aToken);
  237.     if (noErr != err) goto done;
  238.     
  239.     err = AECreateDesc(typeMyText, (Ptr)&aToken, sizeof(aToken), textDesc);
  240.  
  241. done:
  242.     return(err);
  243. }
  244.  
  245.  
  246. OSErr    TextDescFromWindowDesc(AEDesc* windowDesc, AEDesc* textDesc)
  247. {
  248.     TextToken    aToken;
  249.     OSErr        err;
  250.     
  251.     err = TextTokenFromWindowDesc(windowDesc, &aToken);
  252.     if (noErr != err) goto done;
  253.     
  254.     err = AECreateDesc(typeMyText, (Ptr)&aToken, sizeof(aToken), textDesc);
  255.  
  256. done:
  257.     return(err);
  258. }
  259.  
  260.  
  261. void MoveToNonSpace(short *start, short limit, charsHandle myChars)
  262.     // Treats space, comma, full stop, ; and : as space chars
  263.     short x;
  264.  
  265.     while (*start <= limit) {
  266.       x = (**myChars)[*start];
  267.         if (IsWhiteSpace(x))
  268.             (*start) +=1;
  269.         else
  270.             return;
  271.     }
  272. }
  273.     
  274. void    MoveToSpace(short *start, short limit, charsHandle myChars)
  275.     // Treats space,comma, full stop, ; and : as space chars
  276.     short x;
  277.     
  278.     while (*start <= limit)
  279.     {
  280.         x = (**myChars)[*start];
  281.         if (! IsWhiteSpace(x))
  282.             (*start)++;
  283.         else
  284.             return;
  285.     }
  286. }
  287.  
  288. void    MoveToEndOfParagraph(short *start, short limit, charsHandle myChars)
  289.     //    Treats CR as end of paragraph
  290.     short x;
  291.     
  292.     while (*start <= limit)
  293.     {
  294.         x = (**myChars)[*start];
  295.         if (! IsParagraphDelimiter(x))        // had x != CR
  296.             (*start)++;
  297.         else
  298.             return;
  299.     }
  300. }
  301.  
  302.  
  303. // This routine counts the given elementType between startAt and 
  304.  
  305. OSErr    CountTextElements(TEHandle inTextHandle, short startAt,
  306.                                 short forHowManyChars, DescType elementType, short* result)
  307. {
  308.     charsHandle    theChars;
  309.     short       limit,
  310.                 start;
  311.     OSErr        err = noErr;
  312.  
  313.     switch (elementType)
  314.     {
  315.         case cInsertionPoint:    // Always one more insertion location than characters
  316.             *result = forHowManyChars + 1;
  317.             break;
  318.             
  319.         case cChar:            // Easy
  320.             *result = forHowManyChars;
  321.             break;
  322.             
  323.         case cText:    
  324.             *result = 1;
  325.             break;
  326.         
  327.         case cWord:            // Cycle through - counting
  328.         case cParagraph:
  329.             theChars = (charsHandle)(**inTextHandle).hText;
  330.             start = startAt - 1;                    // Convert to zero based
  331.             limit = start + forHowManyChars - 1;    // when passed one based
  332.             *result    = 0;
  333.             MoveToNonSpace(&start, limit, theChars);
  334.             while (start <= limit)
  335.             {
  336.                 (*result)++;
  337.                 switch (elementType)
  338.                 {
  339.                     case cWord:
  340.                         MoveToSpace(&start, limit, theChars);
  341.                         break;
  342.                         
  343.                     case cParagraph:
  344.                         MoveToEndOfParagraph(&start, limit, theChars);
  345.                         break;
  346.                 }
  347.                 MoveToNonSpace(&start, limit, theChars);
  348.             }
  349.             break;
  350.     
  351.         default:
  352.             *result = -1;
  353.             err = errAEBadKeyForm;
  354.     }
  355.     
  356.     return(err);
  357. } // CountTextElements
  358.  
  359. OSErr    GetDescOfNthTextElement(short index, DescType elementType,
  360.                                         TextToken* containerToken, AEDesc* result)
  361. {
  362.     DPtr        docPtr;
  363.     TextToken    theToken;
  364.     short        start,
  365.                 maxChars,
  366.                 elementCount,
  367.                 limit,
  368.                 elementStart;
  369.     charsHandle    theChars;
  370.     OSErr        err;
  371.     
  372.     if (! containerToken)
  373.         return(errAEEmptyListContainer);
  374.  
  375.     docPtr = DPtrFromWindowPtr(containerToken->tokenWindow);
  376.     start = containerToken->tokenOffset - 1;    // Zero based
  377.     maxChars = containerToken->tokenLength;
  378.  
  379.     err = CountTextElements(docPtr->theText, containerToken->tokenOffset,
  380.                                         maxChars, elementType, &elementCount);
  381.     if (noErr != err) return(err);
  382.     
  383.     if (index < 0)                        // Change a negative index to positive
  384.         index = elementCount + index + 1;
  385.         
  386.     if (index > elementCount)            // Got given an index out of range
  387.         return(errAEIllegalIndex);
  388.         
  389.             // Set the window that the token relates to
  390.     theToken.tokenWindow = containerToken->tokenWindow;
  391.  
  392.     switch (elementType)
  393.     {
  394.         case cInsertionPoint:
  395.             theToken.tokenOffset = start + index - 1;
  396.             theToken.tokenLength = 0;
  397.             break;
  398.             
  399.         case cChar:        // Easy - just the start point + the index
  400.             theToken.tokenOffset = start + index;
  401.             theToken.tokenLength = 1;
  402.             break;
  403.             
  404.         case cText:    
  405.             theToken.tokenOffset = start + index;
  406.             theToken.tokenLength = maxChars;
  407.             break;
  408.             
  409.         case cWord:
  410.         case cParagraph:
  411.             theChars = (charsHandle)(**(docPtr->theText)).hText;
  412.             limit = start + maxChars - 1;
  413.             MoveToNonSpace(&start, limit, theChars);
  414.             while ((start <= limit) && (index > 0))
  415.             {
  416.                 index--;
  417.                 elementStart = start;
  418.                 switch (elementType)
  419.                 {
  420.                     case cWord:
  421.                         MoveToSpace(&start, limit, theChars);
  422.                         break;
  423.                         
  424.                     case cParagraph:
  425.                         MoveToEndOfParagraph(&start, limit, theChars);
  426.                         break;
  427.                 }
  428.                 theToken.tokenLength = start - elementStart;
  429.                 MoveToNonSpace(&start, limit, theChars);
  430.             }
  431.             theToken.tokenOffset = elementStart + 1;    // Convert to one based
  432.             break;
  433.     }
  434.  
  435.     err = AECreateDesc(typeMyText, (Ptr)&theToken, sizeof(theToken), result);
  436.  
  437.     return(err);
  438. }
  439.  
  440.  
  441. char    GetTEHChar(TEHandle aTEH, short offset)
  442. {
  443.     char    result;
  444.     
  445.     offset--;        // This is now 0 based
  446.  
  447.     if (offset < 0 || offset >= (*aTEH)->teLength)
  448.         return('\0');
  449.         
  450.     result = *(char *)((*(**aTEH).hText) + offset);
  451.     
  452.     return(result);
  453. }
  454.  
  455. Boolean        IsAtStart(TextToken* theToken)
  456. {
  457.     Boolean    result;
  458.                     // Is at start if offset is at 1
  459.     result = (theToken->tokenOffset == 1);
  460.     
  461.     return(result);
  462. }
  463.  
  464. Boolean        IsAtEnd(TextToken* theToken)
  465. {
  466.     TEHandle    aTEH;
  467.     Boolean        result;
  468.     
  469.     aTEH = TEHandleFromTextToken(theToken);
  470.                     // Does it go to the end?
  471.     result = (theToken->tokenOffset + theToken->tokenLength >= (**aTEH).teLength);
  472.     
  473.     return(result);
  474. }
  475.  
  476. Boolean        IsWhiteSpace(short aChar)
  477. {
  478.     Boolean    result;
  479.  
  480.     result = (aChar == ' ' || aChar == ',' || aChar == '.'
  481.                 || aChar == ':' || aChar == LF || aChar == CR);
  482.              
  483.     return(result);
  484. }
  485.  
  486. Boolean        IsParagraphDelimiter(short aChar)
  487. {
  488.     Boolean    result;
  489.  
  490.     result = (aChar == CR);
  491.              
  492.     return(result);
  493. }
  494.  
  495. Boolean        IsContentsToken(TextToken* theToken)
  496. {
  497.     return(IsAtStart(theToken) && IsAtEnd(theToken));
  498. }
  499.  
  500. Boolean        IsParagraphToken(TextToken* theToken, short* start, short* end)
  501. {
  502.     TEHandle    aTEH;
  503.     OSErr        err;
  504.     short        number;
  505.     Boolean        fStart,
  506.                 fEnd,
  507.                 result;
  508.     
  509. //    if (IsContentsToken(theToken))    // So we don't start and end 
  510. //        return(false);
  511.         
  512.     aTEH = TEHandleFromTextToken(theToken);
  513.         
  514.                                     // What about having CR's before end??
  515.                                     // - then it's just more paragraphs?
  516.                                     // - in STE yes - ahhh it'll be okay
  517.         
  518.     fStart = IsAtStart(theToken) || IsParagraphDelimiter(GetTEHChar(aTEH, theToken->tokenOffset - 1));
  519.     fEnd = IsAtEnd(theToken) || IsParagraphDelimiter(GetTEHChar(aTEH, theToken->tokenOffset + theToken->tokenLength));
  520.     
  521.     if (fStart && fEnd)
  522.     {
  523.         // need to do a count of the paragraphs
  524.         
  525.         err = CountTextElements(aTEH, theToken->tokenOffset,
  526.                             theToken->tokenLength, cParagraph, &number);
  527.  
  528.         // count text elements before it i.e. offset == 0 limit == theToken->tokenOffset
  529.         
  530.         if (IsAtStart(theToken))
  531.             *start = 1;
  532.         else
  533.         {                // From beginning to charracter before start of paragraph
  534.             err = CountTextElements(aTEH, 1,theToken->tokenOffset - 1, cParagraph, start);
  535.             (*start)++;
  536.         }
  537.         
  538.         *end = *start + number - 1;
  539.         
  540.         result = true;
  541.     }
  542.     else
  543.         result = false;
  544.     
  545.     return(result);
  546. }
  547.  
  548. Boolean        IsWordToken(TextToken* theToken, short* start, short* end)
  549. {
  550.     TEHandle    aTEH;
  551.     OSErr        err;
  552.     short        number;
  553.     Boolean        fStart,
  554.                 fEnd,
  555.                 result;
  556.     
  557. //    if (IsContentsToken(theToken) || IsParagraphToken(theToken, start, end))
  558. //        return(false);
  559.     
  560.     aTEH = TEHandleFromTextToken(theToken);
  561.         
  562.                                     // What about having CR's before end??
  563.                                     // - then it's just more paragraphs?
  564.                                     // - in STE yes - ahhh it'll be okay
  565.         
  566.     fStart = IsAtStart(theToken) || IsWhiteSpace(GetTEHChar(aTEH, theToken->tokenOffset - 1));
  567.     fEnd = IsAtEnd(theToken) || IsWhiteSpace(GetTEHChar(aTEH, theToken->tokenOffset + theToken->tokenLength));
  568.     
  569.     if (fStart && fEnd)
  570.     {
  571.         // need to do a count of the words
  572.         
  573.         err = CountTextElements(aTEH, theToken->tokenOffset,
  574.                             theToken->tokenLength, cWord, &number);
  575.  
  576.         // count text elements before it i.e. offset == 0 limit == theToken->tokenOffset
  577.         
  578.         if (IsAtStart(theToken))
  579.             *start = 1;
  580.         else
  581.         {                // From beginning to charracter before start of word
  582.             err = CountTextElements(aTEH, 1, theToken->tokenOffset - 1, cWord, start);
  583.             (*start)++;
  584.         }
  585.         
  586.         *end = *start + number - 1;
  587.         
  588.         result = true;
  589.     }
  590.     else
  591.         result = false;
  592.     
  593.     return(result);
  594. }
  595.  
  596.  
  597. DescType    GetTextTokenType(TextToken* theToken, short* start, short* end)
  598. {
  599.     DescType    result;
  600.     
  601.     *start = *end = -1;                    // Just set to the same value
  602.  
  603.     if (! theToken->tokenLength)
  604.     {
  605.         result = cInsertionPoint;
  606.     }
  607.     else if (IsContentsToken(theToken))
  608.     {
  609.         result = pContents;
  610.     }
  611.     else if (IsParagraphToken(theToken, start, end))
  612.     {
  613.         result = cParagraph;
  614.     }
  615.     else if (IsWordToken(theToken, start, end))
  616.     {
  617.         result = cWord;
  618.     }
  619.     else
  620.     {
  621.         result = cChar;
  622.         *start = theToken->tokenOffset;
  623.         *end = theToken->tokenOffset + theToken->tokenLength - 1;
  624.     }
  625.     
  626.     return(result);
  627. }
  628.  
  629. OSErr    MakeContentsSpecifier(TextToken* theToken, AEDesc* result)
  630. {
  631.     AEDesc        docSpec = {typeNull, NULL},
  632.                 contentsDesc = {typeNull, NULL};
  633.     DescType    propertyID;
  634.     OSErr        err;
  635.  
  636.     err = MakeDocumentObj(theToken->tokenWindow, &docSpec);
  637.     if (noErr != err) goto done;
  638.     
  639.     propertyID = pContents;
  640.     err = AECreateDesc(typeType, (Ptr)&propertyID, sizeof(DescType), &contentsDesc);
  641.     if (err != noErr) goto done;
  642.     err = CreateObjSpecifier(cProperty, &docSpec, formPropertyID, &contentsDesc, false, result);
  643.  
  644. done:
  645.     if (docSpec.dataHandle)
  646.         AEDisposeDesc(&docSpec);
  647.     if (contentsDesc.dataHandle)
  648.         AEDisposeDesc(&contentsDesc);
  649.     
  650.     return(err);
  651. }
  652.  
  653.  
  654. OSErr    MakeAbsoluteTextSpecifier(WindowPtr theWindow, DescType textType, long index, AEDesc* result)
  655. {
  656.     AEDesc        docSpec = {typeNull, NULL},
  657.                 absoluteDesc = {typeNull, NULL};
  658.     OSErr        err;
  659.     
  660.     if (theWindow)
  661.     {
  662.         err = MakeDocumentObj(theWindow, &docSpec);
  663.         if (noErr != err) goto done;
  664.     }
  665.     // else just use the NULL'ed value
  666.  
  667.     err = AECreateDesc(typeLongInteger, (Ptr)&index, sizeof(index), &absoluteDesc);
  668.     if (err != noErr) goto done;
  669.     err = CreateObjSpecifier(textType, &docSpec, formAbsolutePosition,
  670.                                                     &absoluteDesc, false, result);
  671.  
  672. done:
  673.     if (docSpec.dataHandle)
  674.         AEDisposeDesc(&docSpec);
  675.     if (absoluteDesc.dataHandle)
  676.         AEDisposeDesc(&absoluteDesc);
  677.     
  678.     return(err);
  679. }
  680.  
  681.  
  682. OSErr    MakeInsertionPointSpecifier(TextToken* theToken, AEDesc* result)
  683. {
  684.     AEDesc        relativeToSpec,
  685.                 relativeDesc;
  686.     DescType    relativeType;
  687.     OSErr        err;
  688.  
  689.     if (IsAtStart(theToken))            // Before contents (whether there are any or not)
  690.     {
  691.         relativeType = kAEPrevious;
  692.         err = MakeContentsSpecifier(theToken, &relativeToSpec);
  693.     }
  694.     else if (IsAtEnd(theToken))            // After last character
  695.     {
  696.         relativeType = kAENext;
  697.         //err = MakeContentsSpecifier(theToken, &relativeToSpec);
  698.         err = MakeAbsoluteTextSpecifier(theToken->tokenWindow, cChar, -1, &relativeToSpec);
  699.     }
  700.     else                                // Has a character it can go before
  701.     {
  702.         relativeType = kAEPrevious;
  703.         err = MakeAbsoluteTextSpecifier(theToken->tokenWindow, cChar, theToken->tokenOffset, &relativeToSpec);
  704.     }
  705.  
  706.     if (noErr != err) goto done;
  707.     
  708.       err = AECreateDesc(typeEnumerated, &relativeType, sizeof(relativeType), &relativeDesc);
  709.     if (noErr != err) goto done;
  710.     err = CreateObjSpecifier(cInsertionPoint, &relativeToSpec, formRelativePosition,
  711.                                                                    &relativeDesc, false, result);
  712.  
  713. done:
  714.     if (relativeToSpec.dataHandle)
  715.         AEDisposeDesc(&relativeToSpec);
  716.     if (relativeDesc.dataHandle)
  717.         AEDisposeDesc(&relativeDesc);
  718.  
  719.     return(err);
  720. }
  721.  
  722. OSErr    GetIndexSpecifier(TextToken* theToken, DescType textType, long index, AEDesc* result)
  723. {
  724.     OSErr    err;
  725.  
  726.     switch (textType)
  727.     {
  728.         case cInsertionPoint:
  729.             err = MakeInsertionPointSpecifier(theToken, result);
  730.             break;
  731.             
  732.         case pContents:
  733.             err = MakeContentsSpecifier(theToken, result);
  734.             break;
  735.             
  736.         case cParagraph:
  737.         case cWord:
  738.         case cChar:
  739.             err = MakeAbsoluteTextSpecifier(theToken->tokenWindow, textType, index, result);
  740.             break;
  741.         
  742.         default:
  743.             err = errAETypeError;
  744.     }
  745.     
  746.     return(err);
  747. }
  748.  
  749.  
  750. OSErr    GetTextTokenObjectSpecifier(TextToken* theToken, AEDesc* result)
  751. {
  752.     AEDesc        docSpec = {typeNull, NULL},
  753.                 startSpec = {typeNull, NULL},
  754.                 endSpec = {typeNull, NULL},
  755.                 rangeDesc = {typeNull, NULL};
  756.     DescType    textType;
  757.     short        start,
  758.                 end;
  759.     OSErr        err;
  760.     
  761.     textType = GetTextTokenType(theToken, &start, &end);
  762.     
  763.     err = GetIndexSpecifier(theToken, textType, start, &startSpec);
  764.     if (noErr != err) goto done;
  765.  
  766.     if (start != end)        // Sort out rest of range specifier
  767.     {
  768.         err = GetIndexSpecifier(theToken, textType, end, &endSpec);
  769.         if (noErr != err) goto done;
  770.         
  771.         err = CreateRangeDescriptor(&startSpec, &endSpec, false, &rangeDesc);
  772.         if (noErr != err) goto done;
  773.  
  774.         err = MakeDocumentObj(theToken->tokenWindow, &docSpec);
  775.         if (noErr != err) goto done;
  776.  
  777.         err = CreateObjSpecifier(cText, &docSpec, formRange, &rangeDesc, false, result);
  778.     }
  779.     else
  780.         err = AEDuplicateDesc(&startSpec, result);
  781.     
  782. done:
  783.     if (docSpec.dataHandle)
  784.         AEDisposeDesc(&docSpec);
  785.     if (startSpec.dataHandle)
  786.         AEDisposeDesc(&startSpec);
  787.     if (endSpec.dataHandle)
  788.         AEDisposeDesc(&endSpec);
  789.     if (rangeDesc.dataHandle)
  790.         AEDisposeDesc(&rangeDesc);
  791.  
  792.     return(err);
  793. }
  794.